import axios, { AxiosRequestConfig } from 'axios'
import { VERSION, IGNORE_ERROR, FILTER_ERROR, NET_ERROR } from './const'
import { uris, baseUrl } from '~/api'
import { Ref } from 'vue'
import { useRouter } from 'vue-router'

type RequestMethod = 'OPTIONS' | 'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE' | 'TRACE' | 'CONNECT'

/**
 * 定义一个请求类
 */
class Request<T = any> {
  private uri: string //资源路由
  private state?: Ref<boolean> //加载变量
  private opts?: RequestOptions
  private data?: T

  constructor(uri: string, data?: T) {
    this.uri = uri
    this.data = data
  }

  /**
   * 加载过程状态，在发起请求时会设置为true,请求结束后设置为false
   */
  loading(value: Load | undefined) {
    this.state = value
    return this
  }

  options(value: RequestOptions) {
    this.opts = value
    return this
  }

  /**
   * 执行请求，支持泛型约束，第一个约束返回类型，第二个约束参数类型，
   * @param callback 回调函数，只有在使用回调模式时才有过滤器
   * @returns
   */
  async success<K = any>(callback?: Callback<K>): Promise<K> {
    const store = useAppStore()
    const id = httpId(this.uri, this.data)
    if (store.pending.has(id)) {
      console.error('已拦截重复的请求：' + this.uri, id)
      throw IGNORE_ERROR
    }

    await this.checkRequest(this.uri)
    if (this.state) this.state.value = true
    store.pending.add(id)

    const req = prepareRequest(this.uri, this.data, this.opts)

    const response = await axios
      .request<ResObj<K>>({ ...req })
      .then(({ data }) => data)
      .catch(error => {
        if (axios.isCancel(error)) {
          console.warn('已取消请求：' + this.uri)
        } else {
          console.error(error)
          showDialog({ message: 'net error' })
        }
        throw NET_ERROR
      })
      .finally(() => {
        setTimeout(() => {
          store.delPending(id)
          if (this.state) this.state.value = false
        }, 100)
      })
    // 判断结果是否需要过滤，多余1个参数的回调认为不需要过略
    const filter = !callback || callback.length == 1
    if (filter) {
      if (response.code >= 200 && response.code < 300) {
        callback && callback(response.data)
      } else {
        setTimeout(() => {
          showDialog({ message: response.msg })
        }, 300)
        throw FILTER_ERROR
      }
    } else {
      callback && callback(response.data, response)
    }

    return response.data
  }

  /**
   * 在请求前进行检查，判断是否需要刷新token
   */
  async checkRequest(url: string) {
    // 对于白名单中的请求，不检查过期时间和刷新token
    const whiteLists = ['api/login/refresh', 'api/login/token', 'api/login/actionUser']

    if (whiteLists.indexOf(url) < 0 && isTokenExpires()) {
      console.warn('login expiry')
      await this.refreshToken()
    }
  }

  /**
   * 刷新token
   */
  async refreshToken() {
    const store = useAppStore()
    const tgUser = cache.getTgUser()
    const res = await axios.request(
      prepareRequest('api/login/refresh', {
        refreshToken: cache.getRefreshToken(),
        openid: tgUser.id,
        firstName: tgUser.first_name,
        inviter: tgUser.inviter
      })
    )
    if (res.status == 200 && res.data) {
      const resRes = res.data as any
      if (resRes.code == 200) {
        store.initOauth(resRes.data as OAuthRes)
      } else {
        // 如果无法刷新token,且不在登录页面，强制进入登录页

        const route = useRoute()
        const router = useRouter()
        if (!route || route.path != 'logon') {
          router.replace('')
        }
      }
    } else {
      throw NET_ERROR
    }
  }
}

function delayMsg(msg: string) {}

/**
 * 准备请求参数
 */
export function prepareRequest(uri: string, data?: any, options?: RequestOptions) {
  let req: AxiosRequestConfig = {
    url: '',
    data: null as any,
    method: 'GET' as RequestMethod,
    timeout: options?.timeout ? options.timeout : 60000,
    headers: {
      Authorization: cache.getToken() || '',
      'X-Requested-With': 'XMLHttpRequest',
      version: VERSION,
      Lang: localStorage.getItem('localeLang') || 'ruRu',
      Platform: 'telegram'
    }
  }

  let post: any = data || {}
  if (uri.indexOf('{') > 0) {
    const pattern = /\{(.*?)\}/g
    const regexs = uri.match(pattern)
    if (regexs) {
      for (const urlParam of regexs) {
        const key = urlParam.replace('{', '').replace('}', '')
        uri = uri.replace(urlParam, post[key])
        delete post[key]
      }
    }
  }

  post = Object.keys(post).length == 0 ? null : post

  let method = options?.method ? options.method : post ? 'POST' : 'GET'
  req.url = getBaseUrl() + '/' + uri
  req.data = post
  req.method = method as RequestMethod
  return req
}

export function getBaseUrl(): string {
  return import.meta.env.VITE_ENV === 'pro' ? baseUrl : localStorage.getItem('DEV_URL') || baseUrl
}

export function httpId(uri: string, data?: any) {
  return uri + JSON.stringify(data || '')
}

/**
 * 发送一个网络请求，只支持apis配置下的接口请求。其他请求请直接使用axios构建请求
 * @param uri
 * @param options
 * @returns
 */
export function http<T = any>(uri: string, data?: T) {
  return new Request(uri, data)
}

/**
 * 传统字符串切割模式访问
 */
export function request<T = any>(uri: string, data?: T) {
  const uriArray = uri.split('/')
  const contextPath = uris[uriArray[0]].cxpath || uriArray[0]
  let api = (uris[uriArray[0]] || {})[uriArray[1]]
  if (!api) throw 'API定义错误，未找到相关定义' + uri
  return new Request(contextPath + api, data)
}

/**
 *
 * @returns 登录是否已过期
 */
export function isTokenExpires() {
  const expires = cache.getTokenExpires()
  if (expires) {
    return Math.floor(Date.now() / 1000 - 10) > expires
  } else {
    return true
  }
}
